/**
 * ****************************************************************************
 * Project: RoboBridge
 * ----------------------------------------------------------------------------
 * -------------------------- [c]2012 - KrawallMann ---------------------------
 * ---------------------- http://www.krawallmann.de.tc/ -----------------------
 * ****************************************************************************
 * File: RoboBridgeProtocol.java Package:
 * tc.de.krawallmann.robobridge.communication.protocol Version: 1.0 Author(s):
 * KrawallMann
 * ****************************************************************************
 * Description:
 * ****************************************************************************
 */
package tc.de.krawallmann.robobridge.communication.protocol;

import java.util.HashSet;
import java.util.Set;

import tc.de.krawallmann.robobridge.RoboBridge;
import tc.de.krawallmann.robobridge.communication.AbstractCommunicationInterface;
import tc.de.krawallmann.robobridge.communication.AbstractRemoteCommunicationDevice;

/**
 * The Class RoboBridgeProtocol controls the main protocol.
 * 
 * @author KrawallMann
 * @version 1.0
 */
public class RoboBridgeProtocol extends AbstractProtocol {

	/** The robo bridge. */
	protected transient final RoboBridge<? extends AbstractRemoteCommunicationDevice> roboBridge;

	/** The current state type. */
	private transient RoboBridgeProtocolStates currentStateType = RoboBridgeProtocolStates.STANDBY;

	/** The listeners. */
	private transient final Set<RoboBridgeProtocolStateListener> listeners =
			new HashSet<RoboBridgeProtocolStateListener>();

	/** The ping. */
	private long ping;

	/** The ping listeners. */
	private transient final Set<PingListener> pingListeners = new HashSet<PingListener>();

	/** The state. */
	private transient AbstractProtocolState<RoboBridgeProtocol> state;

	/**
	 * Instantiates a new robo bridge protocol.
	 * 
	 * @param communicationInterface
	 *            the communication interface
	 * @param bridge
	 *            the bridge
	 */
	public RoboBridgeProtocol(
			final AbstractCommunicationInterface<? extends AbstractRemoteCommunicationDevice> communicationInterface,
			final RoboBridge<? extends AbstractRemoteCommunicationDevice> bridge) {
		super(communicationInterface);
		roboBridge = bridge;
		state = new StandbyState(this);
		currentStateType=RoboBridgeProtocolStates.STANDBY;
	}

	/**
	 * Adds the ping listener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean addPingListener(final PingListener listener) {
		return pingListeners.add(listener);
	}

	/**
	 * Adds the robo bridge protocol state listener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean addRoboBridgeProtocolStateListener(final RoboBridgeProtocolStateListener listener) {
		return listeners.add(listener);
	}

	/**
	 * Clear ping listeners.
	 */
	public void clearPingListeners() {
		pingListeners.clear();
	}

	/**
	 * Clear robo bridge protocol state listeners.
	 */
	public void clearRoboBridgeProtocolStateListeners() {
		listeners.clear();
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocol#
	 * close()
	 */
	@Override
	public void close() {
		if (currentStateType != RoboBridgeProtocolStates.STANDBY) {
			state = new StandbyState(this);
			currentStateType = RoboBridgeProtocolStates.STANDBY;
			clearIncomingCommandListeners();
			clearRoboBridgeProtocolStateListeners();
			fireProtocolStateChange();
		} else {
			throw new IllegalStateException("Cannot logout when not logged in!");
		}
	}

	/**
	 * Gets the current state type.
	 * 
	 * @return the current state type
	 */
	public RoboBridgeProtocolStates getCurrentStateType() {
		return currentStateType;
	}

	/**
	 * Gets the ping.
	 * 
	 * @return the ping
	 */
	public long getPing() {
		return ping;
	}

	/**
	 * Gets the robo bridge.
	 * 
	 * @return the robo bridge
	 */
	public RoboBridge<? extends AbstractRemoteCommunicationDevice> getRoboBridge() {
		return roboBridge;
	}

	/**
	 * Gets the robo bridge protocol state listener count.
	 * 
	 * @return the robo bridge protocol state listener count
	 */
	public int getRoboBridgeProtocolStateListenerCount() {
		return listeners.size();
	}
	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocol#
	 * getState()
	 */
	@Override
	public AbstractProtocolState<? extends AbstractProtocol> getState() {
		return state;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocol#
	 * open()
	 */
	@Override
	public void open() {
		if (currentStateType == RoboBridgeProtocolStates.STANDBY) {
			state.close();
			state = new HelloState(this);
			currentStateType = RoboBridgeProtocolStates.HELLO;
			fireProtocolStateChange();
		} else {
			throw new IllegalStateException("Cannot login when not logged out!");
		}
	}

	/**
	 * Removes the ping listener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean removePingListener(final PingListener listener) {
		return pingListeners.remove(listener);
	}

	/**
	 * Removes the robo bridge protocol state listener.
	 * 
	 * @param listener
	 *            the listener
	 * @return true, if successful
	 */
	public boolean removeRoboBridgeProtocolStateListener(
			final RoboBridgeProtocolStateListener listener) {
		return listeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * tc.de.krawallmann.robobridge.communication.protocol.AbstractProtocol#
	 * update()
	 */
	@Override
	public void update() {
		if (state.isFailed()) {
			throw new IllegalStateException("The current state reported a failure: "
					+ currentStateType);
		}
		if (state.isComplete()) {
			state.close();
		}
		switch (currentStateType) {
			case STANDBY: {
				break;
			}
			case HELLO: {
				if (state.isComplete()) {
					state = new LoginState(this);
					currentStateType = RoboBridgeProtocolStates.LOGIN;
					fireProtocolStateChange();
				}
				break;
			}
			case LOGIN: {
				if (state.isComplete()) {
					state = new UpdateState(this, roboBridge);
					currentStateType = RoboBridgeProtocolStates.UPDATE;
					fireProtocolStateChange();
				}
				break;
			}
			case UPDATE: {
				break;
			}
			case LOGOUT: {
				if (state.isComplete()) {
					state = new StandbyState(this);
					currentStateType = RoboBridgeProtocolStates.STANDBY;
					fireProtocolStateChange();
				}
			}
			default: {
				throw new IllegalStateException("Invalid state type!");
			}
		}
		state.update();
	}

	/**
	 * Fire protocol state change.
	 */
	protected void fireProtocolStateChange() {
		for (final RoboBridgeProtocolStateListener l : listeners) {
			l.onStateTypeChanged(currentStateType);
		}
	}

	/**
	 * Sets the ping.
	 * 
	 * @param ping
	 *            the new ping
	 */
	protected void setPing(final long ping) {
		this.ping = ping;
		for (final PingListener l : pingListeners) {
			l.onPingChanged(ping);
		}
	}

}
